package com.econ.powercloud.ems.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.econ.powercloud.common.base.facade.util.JacksonUtil;
import com.econ.powercloud.ems.common.enums.EventSourceType;
import com.econ.powercloud.ems.common.pojo.EmsKafkaYxDataDTO;
import com.econ.powercloud.ems.config.EMSStaticMap;
import com.econ.powercloud.ems.constant.SysConstant;
import com.econ.powercloud.ems.dal.entity.EmsFault;
import com.econ.powercloud.ems.dal.entity.EmsProtectSoeEventRecord;
import com.econ.powercloud.ems.pojo.EmsColumnPidVO;
import com.econ.powercloud.ems.pojo.EmsFaultRedisVO;
import com.econ.powercloud.ems.pojo.MeterHisDataVO;
import com.econ.powercloud.ems.pojo.RedisHourDataVO;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author : zjf
 * @Description : 获取设备缓存在redis中的数据
 * @Date : 2021/9/23 14:45
 * @Modified by : zjf
 */
@Component
public class RedisCacheUtil {

    private static final String PREFIX = "lock:";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 通过电表对应的点号集合,获取对应的前一个小时/当天开始/当月开始的数据
     * @param pidConfigs: EmsElectricPidConfig集合
     * @return 第一个key:时间类型(小时,天，月),第二个key:pid
     */
    public Map<String,Map<String, MeterHisDataVO>> getMeterHisDataByPids(List<EmsColumnPidVO> pidConfigs){
        Map<String,Map<String, MeterHisDataVO>> result = Maps.newHashMap();
        if(CollectionUtils.isEmpty(pidConfigs)){
            return result;
        }
        List<MeterHisDataVO> hisDataVOList = Lists.newArrayList();
        Map<String,List<EmsColumnPidVO>> columnPidMap = pidConfigs.stream().collect(Collectors.groupingBy(d->d.getInitDeviceCode()));
        columnPidMap.forEach((k,v)->{
           List<MeterHisDataVO> meterCacheData = this.getMeterCalCacheDataMap(k,v.stream().map(EmsColumnPidVO::getDit).collect(Collectors.toList()));
            if(CollectionUtils.isNotEmpty(meterCacheData)){
                hisDataVOList.addAll(meterCacheData);
            }
        });
        if(CollectionUtils.isEmpty(hisDataVOList)){
            return result;
        }
        Map<String,List<MeterHisDataVO>> maps = hisDataVOList.stream().collect(Collectors.groupingBy(d->d.getTag()));
        maps.forEach((k,v)->{
            Map<String, MeterHisDataVO> pidMap = v.stream().collect(Collectors.toMap(MeterHisDataVO::getPid, d->d,(p1, p2)->p1));
            result.put(k,pidMap);
        });
        return result;
    }

    /**
     * 通过MeterHisDataVO集合去更新redis中的历史数据
     * 此方法用来更新电表中存放的上一个小时,当日开始,当月开始时间的历史数据
     */
    public void updateHisRedisDataByMeterHisDataVO(List<MeterHisDataVO> updateRedisData){
        Map<String,List<MeterHisDataVO>> tagMap = updateRedisData.stream().collect(Collectors.groupingBy(d->d.getTag()));
        tagMap.forEach((tag,v)->{
            v.stream().collect(Collectors.groupingBy(d->d.getInitDeviceCode())).forEach((initDeviceCode,value)->{
                Map<String, String> pidValMap = value.stream().collect(Collectors.toMap(MeterHisDataVO::getPid, d->JacksonUtil.toJSONString(d),(p1, p2)->p1));
                HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
                String cacheKey = SysConstant.METER_HIS + SysConstant.COLON + tag + SysConstant.COLON + initDeviceCode;
                hashOperations.putAll(cacheKey, pidValMap);
            });
        });
    }

    /**
     *  根据按小时小时HashKey获取所有BMU报表数据
     */
    public List<RedisHourDataVO> getBMURedisHistoryDataByHashKey(int lastHour){
        List<RedisHourDataVO> RedisHistoryDataMList = Lists.newArrayList();
        String hashKey = SysConstant.BMS_HIS_HOUR+SysConstant.COLON+lastHour;
        Map<Object,Object> resultMap= stringRedisTemplate.opsForHash().entries(hashKey);
        resultMap.forEach((k,v)->{
            if(!StringUtils.isEmpty((String)v)){
                RedisHourDataVO redisHistoryData = JSON.parseObject(JSON.parse((String)v).toString(), RedisHourDataVO.class);
                RedisHistoryDataMList.add(redisHistoryData);
            }
        });
        return RedisHistoryDataMList;
    }

    public void delByKey(String key){
        // 模糊查找所有的key
        Set<String> keys = stringRedisTemplate.keys(key);
        stringRedisTemplate.delete(keys);
    }

    /**
     *  根据报表类型插入点号历史数据
     */
    public void addBMURedisHistoryDataByHashkey(int nowDateToHour, List<RedisHourDataVO> RedisHistoryDataList){
        String hashkey = SysConstant.BMS_HIS_HOUR+ SysConstant.COLON+nowDateToHour;
        Map<String,String> redisHistoryMap = Maps.newHashMap();
        for (RedisHourDataVO redisHourDataVO : RedisHistoryDataList) {
            redisHistoryMap.put(redisHourDataVO.getPid(), JacksonUtil.toJSONString(redisHourDataVO));
        }
        stringRedisTemplate.opsForHash().putAll(hashkey,redisHistoryMap);
    }

    /**
     *  获取用户操作权限令牌
     */
    public Object getUserOperateAccessToken(String userId){
        String hashKey = String.format(SysConstant.USER_OPERATE_TOKEN,userId);
        return stringRedisTemplate.opsForValue().get(hashKey);
    }

    /**
     *  插入用户操作权限令牌
     */
    public void addUserOperateAccessToken(String userId,long validTime){
        String hashKey = String.format(SysConstant.USER_OPERATE_TOKEN,userId);
        if(validTime <= 0){
            stringRedisTemplate.opsForValue().set(hashKey,validTime+"", 60, TimeUnit.SECONDS);
        }else {
            stringRedisTemplate.opsForValue().set(hashKey,validTime+"",validTime, TimeUnit.SECONDS);
        }
    }

    /**
     *  获取用户操作权限令牌
     */
    public Object deleteUserOperateUserId(String userId){
        String hashKey = String.format(SysConstant.USER_OPERATE_TOKEN,userId);
        return stringRedisTemplate.delete(hashKey);
    }

    /**
     * 设置key过期时间
     * @param key
     * @param timeout
     */
    public void expire(String key, long timeout) {
        stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 获取故障告警的最新的一条记录
     */
    public EmsFault selectLeastFaultData(String deviceCode, String pid, Integer eventSourceType){
        String redisKey = String.format(SysConstant.EMS_FAULT,deviceCode);
        String value = (String)stringRedisTemplate.opsForHash().get(redisKey,String.format("%s-%s",pid,eventSourceType));
        if(value == null){
            return null;
        }
        return JacksonUtil.parseObject(value,EmsFault.class);
    }

    /**
     * 获取故障告警的最新记录
     */
    public List<EmsFault> selectLeastFaultByKafkaYxData(List<EmsKafkaYxDataDTO> yxList){
        List<EmsFault> result = Lists.newArrayList();
        yxList.stream().collect(Collectors.groupingBy(d-> EMSStaticMap.moduleCodeToDeviceMap.get(d.getDeviceCode() + SysConstant.SPOT + (d.getModuleCode()!=null?d.getModuleCode():"def")).getDeviceCode()))
                .forEach((k,v)->{
            String redisKey = String.format(SysConstant.EMS_FAULT,k);
            HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
            List<String> pids = v.stream().map(it -> String.format("%s-%s",it.getPid(), EventSourceType.YX.getCode())).collect(Collectors.toList());
            List<String> results = hashOperations.multiGet(redisKey, pids);
            for (int i = 0; i < pids.size(); i++) {
                String value = results.get(i);
                if (StringUtils.isBlank(value)) {
                    continue;
                }
                result.add(JSONObject.toJavaObject(JSONObject.parseObject(value),EmsFault.class));
            }
        });
        return result;
    }

    /**
     * 获取电表设备前一小时,当天零点，单月开始时的数据
     * @param deviceCode
     * @param dits
     * @return
     */
    private List<MeterHisDataVO> getMeterCalCacheDataMap(String deviceCode, List<String> dits){
        List<MeterHisDataVO> result = Lists.newArrayList();
        Map<String, MeterHisDataVO> hourMap = this.getDeviceDataMap(SysConstant.METER_HIS_HOUR + SysConstant.COLON + deviceCode, deviceCode, dits, MeterHisDataVO.class);
        if(MapUtils.isNotEmpty(hourMap)){
            result.addAll(hourMap.values());
        }
        Map<String, MeterHisDataVO> dayMap = this.getDeviceDataMap(SysConstant.METER_HIS_DAY + SysConstant.COLON + deviceCode, deviceCode, dits, MeterHisDataVO.class);
        if(MapUtils.isNotEmpty(dayMap)){
            result.addAll(dayMap.values());
        }
        Map<String, MeterHisDataVO> monthMap = this.getDeviceDataMap(SysConstant.METER_HIS_MONTH + SysConstant.COLON + deviceCode, deviceCode, dits, MeterHisDataVO.class);
        if(MapUtils.isNotEmpty(monthMap)){
            result.addAll(monthMap.values());
        }
        return result;
    }

    /**
     * 获取设备缓存数据
     */
    private <T> Map<String, T> getDeviceDataMap(String cacheKey, String deviceCode,
                                                List<String> dits, Class<T> clazz) {
        if (CollectionUtils.isEmpty(dits)) {
            return Collections.emptyMap();
        }
        List<String> pids = dits.stream().map(it -> deviceCode + "." + it).collect(Collectors.toList());
        HashOperations<String, String, String> hashOperations = stringRedisTemplate.opsForHash();
        List<String> results = hashOperations.multiGet(cacheKey, pids);
        Map<String, T> resultMap = new HashMap<>();
        for (int i = 0; i < pids.size(); i++) {
            String value = results.get(i);
            if (StringUtils.isBlank(value)) {
                continue;
            }
            resultMap.put(pids.get(i), JacksonUtil.parseObject(value, clazz));
        }
        return resultMap;
    }

    /**
     * 将故障告警数据存入redis
     */
    public void addEmsFaultList(String deviceCode, List<EmsFaultRedisVO> emsFaultRedisVOList){
        String hashKey = String.format(SysConstant.EMS_FAULT,deviceCode);
        Map<String,String> redisHistoryMap = Maps.newHashMap();
        for (EmsFaultRedisVO emsFaultRedisVO : emsFaultRedisVOList) {
            redisHistoryMap.put(emsFaultRedisVO.getFaultPid()+SysConstant.CABLE+emsFaultRedisVO.getFaultResource(), JacksonUtil.toJSONString(emsFaultRedisVO));
        }
        stringRedisTemplate.opsForHash().putAll(hashKey,redisHistoryMap);
    }

    /**
     * 获取Redis锁
     * @param name redis key
     * @param timeoutSec 超时时间（秒）
     * @return
     */
    public Boolean tryLock(String name,Long timeoutSec){
        long threadId = Thread.currentThread().getId();
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(PREFIX+name, PREFIX+threadId , timeoutSec, TimeUnit.SECONDS);
        boolean result = Boolean.TRUE.equals(success);
        return result;
    }

    /**
     * 删除redis锁
     * @param name
     */
    public void unLock(String name){
        String threadId = PREFIX+Thread.currentThread().getId();
        String value = stringRedisTemplate.opsForValue().get(PREFIX+name);
        if (threadId.equals(value)){
            stringRedisTemplate.delete(PREFIX+name);
        }
    }

    /**
     * 将保护事件数据存入redis
     */
    public void addProtectEvent(String deviceCode, Integer actionType, Long gmtCreate
            , EmsProtectSoeEventRecord emsProtectSoeEventRecord) {
        String hashKey = String.format(SysConstant.EMS_FAULT, deviceCode);
        Map<String, String> redisHistoryMap = Maps.newHashMap();
        redisHistoryMap.put(deviceCode + "-" + actionType + "-" + gmtCreate, JacksonUtil.toJSONString(emsProtectSoeEventRecord));
        stringRedisTemplate.opsForHash().putAll(hashKey, redisHistoryMap);
    }

    /**
     * 将保护事件数据存入redis
     */
    public void addSoeEvent(String deviceCode, Integer soeEvent, Long gmtCreate
            , EmsProtectSoeEventRecord emsProtectSoeEventRecord) {
        String hashKey = String.format(SysConstant.EMS_SOEEVENT, deviceCode);
        Map<String, String> redisHistoryMap = Maps.newHashMap();
        redisHistoryMap.put(deviceCode + "-" + soeEvent + "-" + gmtCreate, JacksonUtil.toJSONString(emsProtectSoeEventRecord));
        stringRedisTemplate.opsForHash().putAll(hashKey, redisHistoryMap);
    }

    /**
     * 获取保护事件的最新的一条记录
     */
    public EmsProtectSoeEventRecord selectLeastProtectEvent(String deviceCode, Integer actionType, Long gmtCreate) {
        String hashKey = String.format(SysConstant.EMS_PROTECTEVENT, deviceCode);
        String value = (String) stringRedisTemplate.opsForHash().get(hashKey, String.format("%s-%s-%s", deviceCode, actionType, gmtCreate));
        if (value == null) {
            return null;
        }
        return JacksonUtil.parseObject(value, EmsProtectSoeEventRecord.class);
    }

    /**
     * 获取Soe事件的最新的一条记录
     */
    public EmsProtectSoeEventRecord selectLeastSoeEvent(String deviceCode, Integer soeEvent, Long gmtCreate) {
        String hashKey = String.format(SysConstant.EMS_SOEEVENT, deviceCode);
        String value = (String) stringRedisTemplate.opsForHash().get(hashKey, String.format("%s-%s-%s", deviceCode, soeEvent, gmtCreate));
        if (value == null) {
            return null;
        }
        return JacksonUtil.parseObject(value, EmsProtectSoeEventRecord.class);
    }

    /**
     * 删除保护事件数据
     */
    public void deleteProtectEvent(String deviceCode, Integer actionType, Long gmtCreate) {
        String hashKey = String.format(SysConstant.EMS_FAULT, deviceCode);
        stringRedisTemplate.opsForHash().delete(hashKey, deviceCode + "-" + actionType + "-" + gmtCreate);
    }

    /**
     * 删除SOE事件数据
     */
    public void deleteSoeEvent(String deviceCode, Integer soeEvent, Long gmtCreate) {
        String hashKey = String.format(SysConstant.EMS_SOEEVENT, deviceCode);
        stringRedisTemplate.opsForHash().delete(hashKey, deviceCode + "-" + soeEvent + "-" + gmtCreate);
    }

}
